package main

import (
	"os"
	"os/signal"
	"syscall"
	"time"

	"gameServer/config"      // 配置模块：负责加载、管理文件配置和环境变量配置
	"gameServer/eventbus"    // 事件总线模块：实现模块间解耦的通信机制（如会话创建/匹配事件）
	"gameServer/gamesession" // 游戏会话模块：管理会话生命周期、玩家匹配等核心逻辑
	"gameServer/grpc"        // gRPC服务模块：将会话功能封装为远程调用接口，供外部客户端访问
	"gameServer/log"         // 日志模块：负责系统日志的输出、轮转等功能
)

/*
整个程序是一个游戏服务器的核心启动流程，按 “配置加载→组件初始化→服务启动→优雅退出” 的逻辑执行，各组件职责和依赖关系如下：
配置层（config）：作为所有组件的 “参数来源”，整合文件配置（静态）和环境变量配置（动态），避免硬编码，方便多环境（开发 / 测试 / 生产）切换。
日志层（log）：基于配置初始化，为所有模块提供统一的日志输出能力，是问题排查和系统监控的基础。
通信层（eventbus）：作为模块间的 “消息枢纽”，解耦会话、匹配、日志等模块（如会话创建后发布事件，日志模块订阅事件记录日志，无需直接调用）。
业务核心层（gamesession）：
会话管理器（SessionManager）：管理所有游戏会话的生命周期（创建、查询、清理）；
匹配器（Matcher）：按策略（随机）为玩家组队，匹配成功后将玩家分配到会话。
对外接口层（grpc）：将业务核心功能封装为 gRPC 接口，供外部客户端（如游戏客户端）远程调用，实现 “服务器 - 客户端” 的通信。
退出处理：监听系统信号（Ctrl+C），避免程序被强制终止，后续可扩展资源释放逻辑（如关闭数据库连接、保存会话状态）。
*/

// 启动服务并处理退出信号，确保程序稳定运行和优雅关闭
func main() {
	// -------------------------- 1. 加载配置：从文件和环境变量读取系统配置 --------------------------
	// 1.1 创建文件配置对象：指定配置文件路径（JSON格式），用于读取静态配置（如日志路径、数据库地址）
	// 参数：配置文件的绝对路径（此处为Windows路径，Linux/macOS需改为对应路径格式）
	fileConfig := config.NewFileConfig("d:\\vscode_\\myFile\\GOLANG\\game_server\\config\\config.json")
	// 加载文件配置：解析JSON内容到fileConfig对象，加载失败则记录致命错误并终止程序（Fatalf会退出）
	if err := fileConfig.Load(); err != nil {
		log.Fatalf("Failed to load config: %v", err)
	}

	// 1.2 创建环境变量配置对象：指定环境变量前缀为"GAME"，用于读取动态/敏感配置（如数据库密码）
	// 作用：环境变量配置优先级通常高于文件配置，可覆盖文件中的相同项（如GAME_LOG_LEVEL=debug覆盖文件中的log.level）
	envConfig := config.NewEnvConfig("GAME")

	// 1.3 创建配置管理器：整合文件配置和环境变量配置，提供统一的配置获取入口
	// 参数：按优先级顺序传入配置源（此处envConfig优先级可能高于fileConfig，具体看Manager实现）
	manager := config.NewManager(envConfig, fileConfig)

	// 1.4 创建网关配置对象：网关配置包含日志、数据库、网络等模块的具体参数
	gateConfig := config.NewGateConfig()
	// 从配置管理器加载网关配置：将manager中的配置（文件+环境变量）映射到gateConfig的字段
	gateConfig.LoadFromManager(manager)

	// -------------------------- 2. 初始化日志系统：基于配置启动日志功能 --------------------------
	// 构建日志配置结构体：从gateConfig中提取日志相关参数，用于初始化日志
	logConfig := &log.LogConfig{
		Level:      gateConfig.Log.Level,      // 日志级别（如debug/info/warn，控制日志输出详细程度）
		Format:     gateConfig.Log.Format,     // 日志格式（如text/json，text适合调试，json适合日志分析）
		Output:     gateConfig.Log.Output,     // 日志输出目标（如stdout/filesource，stdout输出到控制台，file输出到文件）
		FilePath:   gateConfig.Log.FilePath,   // 日志文件路径（仅Output为file时生效，如"logs/server.log"）
		MaxSize:    gateConfig.Log.MaxSize,    // 单个日志文件最大大小（单位MB，触发日志轮转）
		MaxAge:     gateConfig.Log.MaxAge,     // 日志文件最大保存时间（如7*24h，超过则清理旧日志）
		MaxBackups: gateConfig.Log.MaxBackups, // 日志文件最大备份数（超过则删除最旧的备份）
	}

	// 初始化日志系统：按logConfig配置启动日志，初始化失败则终止程序
	if err := log.InitFromConfig(logConfig); err != nil {
		log.Fatalf("Failed to initialize log system: %v", err)
	}

	// -------------------------- 3. 初始化事件总线：实现模块间解耦通信 --------------------------
	// 创建内存型事件总线实例：事件总线用于模块间“发布-订阅”通信（如会话创建后发布事件，匹配器订阅事件）
	// 优点：模块无需直接依赖，通过事件传递信息，降低耦合（如会话模块无需知道日志模块的存在）
	eventBus := eventbus.NewMemoryEventBus()

	// 创建事件消费者实例：负责订阅并处理事件总线中的事件（如处理“会话创建”事件并记录日志）
	eventConsumer := eventbus.NewEventConsumer(eventBus)
	// 启动事件消费者：开启独立goroutine监听事件，处理订阅的事件逻辑
	eventConsumer.Start()

	// -------------------------- 4. 初始化会话管理器：管理所有游戏会话生命周期 --------------------------
	// 构建会话管理器配置：定义会话管理的核心规则
	sessionManagerConfig := gamesession.ManagerConfig{
		MaxSessions:     100,              // 系统允许的最大会话数（超过则无法创建新会话，避免资源耗尽）
		CleanupInterval: 5 * time.Minute,  // 过期会话清理间隔（每5分钟检查并删除空闲超时的会话）
		SessionTimeout:  30 * time.Minute, // 会话空闲超时时间（空闲30分钟的会话视为过期，会被清理）
	}
	// redisDB, err := database.GlobalManager().GetDatabase("session_redis")
	// if err != nil {
	// 	log.Fatalf("Failed to get Redis database: %v", err)
	// }
	// redisStorage, ok := redisDB.(*database.RedisDatabase)
	// if !ok {
	// 	log.Fatalf("Redis database type error")
	// }
	// 创建会话管理器实例：负责创建、查询、移除会话，以及自动清理过期会话
	// 参数：eventBus（用于发布会话生命周期事件）、sessionManagerConfig（会话管理规则）
	sessionManager := gamesession.NewSessionManager(eventBus, sessionManagerConfig)

	// -------------------------- 5. 初始化匹配器：实现玩家自动匹配组队 --------------------------
	// 创建匹配器实例：负责管理玩家匹配请求，按指定策略将玩家分配到游戏会话
	// 参数：
	//   eventBus：发布匹配相关事件（如“匹配开始”“匹配成功”）
	//   sessionManager：匹配成功后将玩家分配到会话（创建新会话或加入已有会话）
	//   gamesession.StrategyRandom：匹配策略为“随机匹配”（不考虑玩家技能、地区，随机组队）
	matcher := gamesession.NewMatcher(eventBus, sessionManager, gamesession.StrategyRandom)

	// 启动匹配器的定时匹配循环：每隔5秒执行一次匹配逻辑（检查匹配队列，为满足条件的玩家组队）
	// 作用：避免实时监听队列，通过定时轮询降低资源消耗，平衡匹配效率和性能
	matcher.StartMatchingLoop(5 * time.Second)

	// -------------------------- 6. 启动gRPC服务器：提供远程调用接口 --------------------------
	// 打印gRPC服务器启动日志：告知用户服务器监听的端口
	log.Info("Starting gRPC server on port 50051")

	// 启动gRPC服务器：监听50051端口（gRPC默认端口），将会话管理器传递给服务，供接口调用
	// 作用：外部客户端（如游戏客户端、管理后台）可通过gRPC调用会话功能（创建会话、添加玩家等）
	// 启动失败则记录致命错误并终止程序
	if err := grpc.StartGrpcServer("50051", sessionManager); err != nil {
		log.Fatalf("Failed to start gRPC server: %v", err)
	}

	// -------------------------- 7. 阻塞主goroutine：保持程序运行并处理退出信号 --------------------------
	// 打印服务器启动成功日志：告知用户服务器已就绪，等待客户端连接
	log.Info("Server started successfully, press Ctrl+C to shut down")

	// 创建退出信号通道：缓冲大小为1，用于接收系统退出信号（如Ctrl+C触发的SIGINT）
	quit := make(chan os.Signal, 1)

	// 注册需要监听的信号：SIGINT（终端中断信号，如Ctrl+C）、SIGTERM（进程终止信号，如kill命令）
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)

	// 阻塞主goroutine：直到收到退出信号（<-quit会一直等待，直到有信号写入通道）
	<-quit

	// 收到退出信号后，打印关闭日志：告知用户服务器开始优雅关闭（后续可扩展资源释放逻辑，如关闭数据库连接）
	log.Info("Shutting down server...")
}
